/*
 * Hash.cpp
 *
 *  Created on: Oct 12, 2012
 *      Author: romina
 */

#include "Hash.h"

Hash::Hash(string path) {
	this->file_log = new ArchivoDeTexto("log_hash.txt", W);
	this->file_tabla = new Tabla(path + PATH_TABLA);
	this->file_bloques = new ManejadorDeCubo(path + PATH_CUBOS);
	if(this->file_tabla->vacia()){
		Cubo cubo;
		int id_cubo = 0;
		cubo.set_td(1);
		cubo.set_id(id_cubo);
		this->file_bloques->actualizar_cubo(cubo, cubo.get_id());
		/********************************/
		Cubo * guardado = this->file_bloques->obtener_cubo(id_cubo);
		this->file_log->escribirLinea("guarde el bloque vacio => " + guardado->toStringInfo());
		this->file_log->escribirLinea("Inserto primer referencia en la tabla: cubo " + Palabra::intToString(id_cubo));
		delete(guardado);
		/********************************/
		this->file_tabla->referencia_inicial(id_cubo);

	}
}


int Hash::calcular_dispersion(int clave){
	return clave % this->file_tabla->get_tamanio();
}

int Hash::get_cantidad_cubos(){
	return this->file_bloques->get_cantidad_cubos();
}

list<RecordFrase> Hash::get_frases_en_cubo(int id){
	return this->file_bloques->obtener_cubo(id)->listar_registros();
}

void Hash::redispersar(Cubo &cubo_i, Cubo &cubo_f){
	list<RecordFrase> registros;
	list<RecordFrase> ::iterator itrecord;

	registros = cubo_i.listar_registros();

	for(itrecord = registros.begin(); itrecord != registros.end(); ++itrecord){

		int id_registro = (*itrecord).getId();
		int pos_cubo_tabla = this->calcular_dispersion(id_registro);
		int id_cubo = this->file_tabla->get_id_cubo(pos_cubo_tabla);

		if(cubo_i.get_id() != id_cubo){
			cubo_i.eliminar_registro((*itrecord).getId());
			cubo_f.insertar_registro((*itrecord));
		}
	}
}


void Hash::overflow(RecordFrase record, Cubo cubo_overflow){
	Cubo cubo_duplicado;
	int dispersion_inicial = cubo_overflow.get_td();


	int id_cubo_duplicado = this->file_bloques->get_id_proximo_cubo();
	int posicion = this->calcular_dispersion(record.getId());

	cubo_overflow.duplicar_td();
	cubo_duplicado.set_td(cubo_overflow.get_td());
	cubo_duplicado.set_id(id_cubo_duplicado);

	if(dispersion_inicial == this->file_tabla->get_tamanio()){
		this->file_log->escribirLinea("DUPLICAMOS LA TABLA");
		this->file_tabla->duplicar();
		this->file_tabla->reemplazar(posicion, cubo_duplicado.get_id());
	}
	else{
		this->file_log->escribirLinea("REEMPLAZAMOS REFERENCIAS");
		this->file_tabla->reemplazar_desde(posicion, cubo_duplicado.get_id(), cubo_duplicado.get_td());
	}

	this->redispersar(cubo_overflow, cubo_duplicado);
	this->file_bloques->actualizar_cubo(cubo_duplicado, cubo_duplicado.get_id());
	this->file_bloques->actualizar_cubo(cubo_overflow, cubo_overflow.get_id());

	/********************************/
	Cubo * guardado_overflow = this->file_bloques->obtener_cubo(cubo_overflow.get_id());
	Cubo * guardado_duplicado = this->file_bloques->obtener_cubo(cubo_duplicado.get_id());
	this->file_log->escribirLinea("cubo_overflow final" +  guardado_overflow->toStringInfo());
	this->file_log->escribirLinea("cubo_duplicado final " + guardado_duplicado->toStringInfo() );
	delete(guardado_duplicado);
	delete(guardado_overflow);
	/********************************/

}

void Hash::insertar_registro(RecordFrase record){

	try{
		this->obtener_registro(record.getId());
		throw RecordDuplicatedException("Ya existe la clave, debe ingresar otra clave o bien modificar el registro existente");
	}catch(RecordNotFoundException &e){

	}

	this->file_log->escribirLinea("");
	this->file_log->escribirLinea("");
	this->file_log->escribirLinea("SE INSERTA NUEVO REGISTRO");
	this->file_log->escribirLinea("=========================");

	this->file_log->escribirLinea("TABLA: " + this->file_tabla->toString());
	int pos_cubo = this->calcular_dispersion(record.getId());
	this->file_log->escribirLinea("pos cubo en tabla " + Palabra::intToString(pos_cubo));
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);
	this->file_log->escribirLinea("numero cubo " + Palabra::intToString(id_cubo));
	Cubo * cubo = this->file_bloques->obtener_cubo(id_cubo);

	this->file_log->escribirLinea("cubo deserealizado " + cubo->toStringInfo());

	if(!cubo->overflow(record.getTamanio())){
		this->file_log->escribirLinea("NO FUE OVERFLOW");
		cubo->insertar_registro(record);
		this->file_bloques->actualizar_cubo(*cubo, cubo->get_id());

		/********************************/
		Cubo * guardado = this->file_bloques->obtener_cubo(cubo->get_id());
		this->file_log->escribirLinea("ESTADO FINAL DEL CUBO => " + guardado->toStringInfo());
		delete(guardado);
		/********************************/


	}
	else{
		this->file_log->escribirLinea("FUE OVERFLOW");
		this->overflow(record, *cubo);
		delete(cubo);
		this->insertar_registro(record);
	}
}


RecordFrase Hash::obtener_registro(int clave){
	RecordFrase record;
	int pos_cubo = this->calcular_dispersion(clave);
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);
	Cubo * cubo = this->file_bloques->obtener_cubo(id_cubo);
	cubo->buscar_registro(clave, record);
	delete (cubo);
	return record;
}


void Hash::modificar_registro(RecordFrase record){
	this->borrar_registro(record.getId());
	this->insertar_registro(record);
}


void Hash::borrar_registro(int clave){
	int pos_cubo = this->calcular_dispersion(clave);
	int id_cubo = this->file_tabla->get_id_cubo(pos_cubo);
	Cubo * cubo = this->file_bloques->obtener_cubo(id_cubo);
	cubo->eliminar_registro(clave);
	this->file_bloques->actualizar_cubo(*cubo, cubo->get_id());
	delete(cubo);

}

string Hash::toString(){
	int cant_cubos = this->file_bloques->get_cantidad_cubos();
	string cadena = "";
	for(int i = 0; i < cant_cubos; ++i){
		Cubo * cubo = this->file_bloques->obtener_cubo(i);
		cadena.append(cubo->toString());
		delete(cubo);
	}
	return cadena;

}

void Hash::show(){
	int cant_cubos = this->file_bloques->get_cantidad_cubos();
	cout << "CANTIDAD DE CUBOS: "<< cant_cubos << endl;
	string cadena = "";
	for(int i = 0; i < cant_cubos; ++i){
		Cubo * cubo = this->file_bloques->obtener_cubo(i);
		cout << cubo->toStringInfo() << endl;
		delete(cubo);
	}


}

void Hash::download(string path){
	ArchivoDeTexto archivo(path, W);

	int cant_cubos = this->file_bloques->get_cantidad_cubos();
	archivo.escribirLinea("REPORTE DE FRASES EN ARCHIVO");
	archivo.escribirLinea("");
	archivo.escribirLinea("");
	archivo.escribirLinea("CANTIDAD DE CUBOS: " + Palabra::intToString(cant_cubos));

	string cadena = "";
	for(int i = 0; i < cant_cubos; ++i){
		Cubo * cubo = this->file_bloques->obtener_cubo(i);
		archivo.escribirLinea(cubo->toString());
		delete(cubo);
	}
}

Hash::~Hash() {
	delete this->file_bloques;
	delete this->file_tabla;
	delete this->file_log;
}




